package com.umfwechat.util;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Hashtable;



@SuppressWarnings("unchecked")
public class SequenceUtil {

    private static SequenceUtil su = null;
	private static final Hashtable serials = new Hashtable();
    private static final Hashtable fchannels = new Hashtable();

    private SequenceUtil() {
    }

    public static synchronized SequenceUtil getInstance() {
        if (su == null)
            su = new SequenceUtil();
        return su;
    }
 
	public synchronized long getSequence4File(String name) {
        String appKey = name + ".seq";
        FileChannel fc = null;
        MappedByteBuffer serial = null;
        try {
        	fc = (FileChannel) fchannels.get(appKey);
        	serial = (MappedByteBuffer) serials.get(appKey);
            if (serial == null) {
                RandomAccessFile RAFile = new RandomAccessFile(appKey, "rw");
                if (RAFile.length() < 8) {
                    RAFile.writeLong(0);
                }
                fc = RAFile.getChannel();

                int size = (int) fc.size();

                serial = fc.map(FileChannel.MapMode.READ_WRITE, 0, size);
                
                fchannels.put(appKey, fc);
                serials.put(appKey, serial);
	        }
	        
	        FileLock flock = fc.lock();
	        serial.rewind();
	        long serno = serial.getLong();
            serno++;
	        serial.flip();
	        serial.putLong(serno);
	        flock.release();

	        return serno;
        } catch (IOException e) {
            e.printStackTrace();
            return -1;
        }
    }
	
	public synchronized long getSequence4File(String name, long initVal) {
        String appKey = name + ".seq";
        FileChannel fc = null;
        MappedByteBuffer serial = null;
        try {
        	fc = (FileChannel) fchannels.get(appKey);
        	serial = (MappedByteBuffer) serials.get(appKey);
            if (serial == null) {
                RandomAccessFile RAFile = new RandomAccessFile(appKey, "rw");
                if (RAFile.length() < 8) {
                    RAFile.writeLong(0);
                }
                fc = RAFile.getChannel();

                int size = (int) fc.size();

                serial = fc.map(FileChannel.MapMode.READ_WRITE, 0, size);
                
                fchannels.put(appKey, fc);
                serials.put(appKey, serial);
	        }
	        
	        FileLock flock = fc.lock();
	        serial.rewind();
	        long serno = serial.getLong();
            serno++;
            if (serno < initVal) {
				serno = initVal + 1;
			} 
	        serial.flip();
	        serial.putLong(serno);
	        flock.release();

	        return serno;
        } catch (IOException e) {
            e.printStackTrace();
            return -1;
        }
    }
	
	public long getSequence(String name, long initVal) throws Exception {
		
		boolean isValidate = false;
		
		Long id = null;
		do {
			id = SequenceUtil.getInstance().getSequence4File(name, initVal);
			if (id.longValue() >= initVal) {
				isValidate = true;
			}
		} while (!isValidate);
		
		if (null == id) {
			throw new Exception("获取序列数失败!");
		}
		
		return id;
	}
	
	/**
	 * 
	 * desc:直接返回字符串名
	 * <p>创建人：zhaohonglin , 2017-4-26 下午5:00:53</p>
	 */
	public synchronized long getSequence(String name) {
		return Serial.next(name);
	}
	
	public long getSequence(String name, long initVal, int width) throws Exception {
		
		boolean isValidate = false;
		
		Long id = null;
		do {
			id = SequenceUtil.getInstance().getSequence4File(name);
			if (id.longValue() >= initVal) {
				isValidate = true;
			}
		} while (!isValidate);
		
		if (null == id) {
			throw new Exception("获取序列数失败!");
		}
		
        String is = id.toString();
        if (is.length() < width) { 
        	throw new Exception("序列数已满!");
        }
		
		return id;
	}

	public static String formatSequence(long seq, int width) {
	        Long lg = new Long(seq);
	        String is = lg.toString();
	    if (is.length() < width) { 
	       while (is.length() < width)
	         is = "0" + is;
	    } else { 
	        is = is.substring(is.length() - width, is.length());
	    }
	    return is;
	}
	
	public static String getSeq(String name, int width) throws Exception {
		Long id = SequenceUtil.getInstance().getSequence(name, 300);
		String num= SequenceUtil.formatSequence(id, width);
		return num;
	}
	   
    public static void main(String[] args) throws Exception {
		  
		Long id = 0L;
		try {
			id = SequenceUtil.getInstance().getSequence("sp", 100000000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println(getSeq("sp", 3));
//		  id = SequenceUtil.getInstance().getSequence4File("sp");
//		  String num= SequenceUtil.formatSequence(id, 16);
		  System.out.println(id);
//		  try {
//			System.out.println(getSeq("sp",4));
//		} catch (Exception e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}
}
